/*
   validation.js
   BY:
        Danny Goodman
   
   DESCRIPTION:
        validations.js is a library of client-side JavaScript data
        entry validation routines. To include this file with an HTML
        document, enter the following statement within the <HEAD>...</HEAD>
        tag pair:
           <SCRIPT LANGUAGE="JavaScript" SRC="validation.js">
        This library file should be in the same directory as the HTML
        files that call upon its routines.
        
        Input fields in a form include onChange event handlers that invoke
        the validate() function (or parent.validate() if the .js file is
        included in the framesetting document), passing as arguments:
        	window
        	this
        	a string of the name of the specific validation routine
        For example:

     <INPUT TYPE="text" NAME="exp_date" ... onChange="validate(window, this,'isPhone')">

        To include multiple validations for a given field, add more validation
        name strings as arguments to the event handler:

            <...onChange="validate(window, this,'<valName1>'[...'valNamen'])">
        
*/

// global variables--reference to input field and owning frame
// being validated set when validate() called
var gField
var gFrame

/************************************
   Begin validation routines
************************************/

// input field is not empty
function isNotEmpty() {
	if (gField.value == "" || gField.value == null) {
		alert("The selected field cannot be left empty.")
		gField.focus()
		gField.select()
		return false
	}
	gField.value = gField.value.toUpperCase()
	return true
}

// input value is a positive integer of any length
function isPositiveInteger() {
	var inputStr = gField.value
	if (inputStr.length == 0) return true
	for (var i = 0; i < inputStr.length; i++) {
		var oneChar = inputStr.charAt(i)
		if (oneChar < "0" || oneChar > "9") {
			alert("The selected field must be a positive, whole number (no letters or punctuation are allowed).")
			gField.focus()
			gField.select()
			return false
		}
	}
	return true
}

// input value is a positive integer, max 8 characters (and strip commas, periods, and "$")
// then re-insert commas for display.
function isDollarsOnly8Commas() {
	var inputStr = gField.value
	if (inputStr.length == 0) return true
	inputStr = stripCommasAndDoll(gField.value)
	inputStr = stripCents(inputStr)
	gField.value = inputStr

	if (isPositiveInteger()) {
		if (inputStr.length > 8) {
			alert("The selected field must be less than 100,000,000.")
			gField.focus()
			gField.select()
			return false
		}
		gField.value = reinsertCommas(inputStr)
		return true
	}
	alert("Check the dollar amount entered.")
	gField.focus()
	gField.select()
	return false
}

// input value is a U.S. state abbreviation; set entered value to all uppercase
// also set companion field (NAME="<xxx>_expand") to full state name
function isUSState() {
	var inputStr = gField.value.toUpperCase()
	if (inputStr.length > 0 && USStates[inputStr] == null) {
		var msg = ""
		var firstChar = inputStr.charAt(0)
		if (firstChar == "A") {
			msg += "\n(Alabama = AL; Alaska = AK; Arizona = AZ; Arkansas = AR)"
		}
		if (firstChar == "D") {
			msg += "\n(Delaware = DE; District of Columbia = DC)"
		}
		if (firstChar == "I") {
			msg += "\n(Idaho = ID; Illinois = IL; Indiana = IN; Iowa = IA)"
		}
		if (firstChar == "M") {
			msg += "\n(Maine = ME; Maryland = MD; Massachusetts = MA; Michigan = MI; Minnesota = MN; Mississippi = MS; Missouri = MO; Montana = MT)"
		}
		if (firstChar == "N") {
			msg += "\n(Nebraska = NE; Nevada = NV)"
		}
		alert("Check the spelling of the state abbreviation." + msg)
		gField.focus()
		gField.select()
		return false
	}
	gField.value = inputStr

	/* comment out next two lines to 'turn off' state expansion functionality */
	var expandField = eval("window." + gFrame.name + ".document.forms[0]." + gField.name + "_expand")
	expandField.value = (inputStr.length == 2)? USStates[inputStr] : ""
	/* end of turning off state expansion functionality */

	return true
}

// checks for strings of maximum length
function maxString15() {
	return maxString(15)
}
function maxString25() {
	return maxString(25)
}
function maxString30() {
	return maxString(30)
}
function maxString35() {
	return maxString(35)
}


// check input date format, and repair if possible
function isDateFormat() {
	var inputStr = gField.value
	// convert hyphen delimiters to slashes
	while (inputStr.indexOf("-") != -1) {
		inputStr = replaceString(inputStr,"-","/")
	}
	var delim1 = inputStr.indexOf("/")
	var delim2 = inputStr.lastIndexOf("/")
	if (delim1 != -1 && delim1 == delim2) {
		// there is only one delimiter in the string
		alert("The date entry is not in an acceptable format.\n\nYou can enter dates in the following formats: mmddyyyy, mm/dd/yyyy, or mm-dd-yyyy.  (If the month or date data is not available, enter \'01\' in the appropriate location.)")
		gField.focus()
		gField.select()
		return false
	}
	if (delim1 != -1) {
		// there are delimiters; extract component values
		var mm = parseInt(inputStr.substring(0,delim1),10)
		var dd = parseInt(inputStr.substring(delim1 + 1,delim2),10)
		var yyyy = parseInt(inputStr.substring(delim2 + 1, inputStr.length),10)
	} else {
		// there are no delimiters; extract component values
		var mm = parseInt(inputStr.substring(0,2),10)
		var dd = parseInt(inputStr.substring(2,4),10)
		var yyyy = parseInt(inputStr.substring(4,inputStr.length),10)
	}
	if (isNaN(mm) || isNaN(dd) || isNaN(yyyy)) {
		// there is a non-numeric character in one of the component values
		alert("The date entry is not in an acceptable format.\n\nYou can enter dates in the following formats: mmddyyyy, mm/dd/yyyy, or mm-dd-yyyy.")
		gField.focus()
		gField.select()
		return false
	}
	if (mm < 1 || mm > 12) {
		// month value is not 1 thru 12
		alert("Months must be entered between the range of 01 (January) and 12 (December).")
		gField.focus()
		gField.select()
		return false
	}
	if (dd < 1 || dd > 31) {
		// date value is not 1 thru 31
		alert("Days must be entered between the range of 01 and a maximum of 31 (depending on the month and year).")
		gField.focus()
		gField.select()
		return false
	}
	if (yyyy < 100) {
		// entered value is two digits, which we allow for 1930-2029
		if (yyyy >= 30) {
			yyyy += 1900
		} else {
			yyyy += 2000
		}
	}
	gField.value = monthDayFormat(mm) + "/" + monthDayFormat(dd) + "/" + yyyy
	return true
}


// Minus 5\Plus 10 years date advisory
function isM5_P10Date() {
	if (gField.value.length == 0) return true
	var thisYear = getYear()
	return isDate((thisYear - 5),(thisYear + 10))
}


// checks for single-letter Y, N, or U
function isNYU() {
	var inputStr = gField.value.toUpperCase()
	if (inputStr.length != 1 || "NYU".indexOf(inputStr) == -1) {
		alert("Enter one letter only:\n   N(o)\n   Y(es)\n   U(nknown)")
		gField.focus()
		gField.select()
		return false	
	}
	gField.value = inputStr
	return true
}

// checks for first five digits of a ZIP code
function isZip() {
	if (gField.value.length == 0) return true
	var inputStr = gField.value
	if (isPositiveInteger()) {
		if (inputStr.length != 5) {
			alert("Enter the first five digits of the ZIP code in this field.")
			gField.focus()
			gField.select()
			return false
		}
		return true
	}
	return false
}


// checks for valid phone number; expands numbers-only with hyphens
function isPhone() {
	var inputStr = gField.value
	if (inputStr.length == 0) return true
	inputStr = stripHyphens(inputStr)
	inputStr = stripSlashParens(inputStr)
	for (var i = 0; i < inputStr.length; i++) {
		var oneChar = inputStr.charAt(i)
		if (oneChar < "0" || oneChar > "9") {
			alert("A phone number must be numbers only, including Area Code.  The entry format is \'212-555-1212\' or \'2125551212\'.")
			gField.focus()
			gField.select()
			return false
		}
	}
	if (inputStr.length != 10) {
			alert("Your phone number entry contains " + inputStr.length + " numbers instead of the required 10. Be sure to include Area Code.")
			gField.focus()
			gField.select()
			return false
	}
	inputStr = restorePhoneHyphens(inputStr)
	gField.value = inputStr
	return true
}


// Check components of Social Security and Employment ID Numbers
// First component of SSN requires range check
function isSSN1() {
	var inputStr = gField.value
	if (inputStr == null || inputStr == "") {return true}
	if (isNum(3)) {
		if (parseInt(inputStr) >= 800) {
			alert("The first three digits of a Social Security number must be below 800.")
			gField.focus()
			gField.select()
			return false
		}
		if (inputStr == "000") {
			alert("All zeros are not allowed in any Social Security number field.")
			gField.focus()
			gField.select()
			return false
		}
		return true
	}
	return false
}

// Other components can be generalized
function isSSN2() {
	return isTINComponent(2, "Social Security")
}
function isSSN3() {
	return isTINComponent(4, "Social Security")
}

// checks an entry against a parallel, duplicate entry to
// confirm that correct data has been entered
// Parallel field name must be the main field name plus "_xcfm"
function isConfirmed() {
	var inputStr = gField.value
	// flag for whether field under test is primary (true) or confirmation field
	var primary = (gField.name.indexOf("_xcfm") == -1)
	if (primary) {
		// clear the confirmation field if primary field is changed
		var xcfmField = eval("window." + gFrame.name + ".document.forms[0]." + gField.name + "_xcfm")
		var xcfmValue = xcfmField.value
		if (inputStr != xcfmValue) {
			xcfmField.value = ""
			return true
		}
	} else {
		var xcfmField = eval("window." + gFrame.name + ".document.forms[0]." + gField.name.substring(0,(gField.name.length-5)))
	}
	var xcfmStr = xcfmField.value
	if ((primary && xcfmStr != "") || (!primary)) {
		if (inputStr != xcfmStr) {
			alert("The main and confirmation entry field contents do not match. Both fields must have EXACTLY the same content to be accepted by the database.")
			gField.focus()
			gField.select()
			return false	
		}
	}
	return true
}

/************************************************
   Begin utility validation and support routines
************************************************/

// generic maximum string length checker (called by dispatched validation functions)
function maxString(len) {
	var inputStr = gField.value.toUpperCase()
	if (inputStr.length > len) {
		if (!confirm("This field is limited to " + len + " characters long. Should the system accept the first " + len + " characters of your entry, and ignore the rest?")) {
			gField.focus()
			gField.select()
			return false
		}
		inputStr = inputStr.substring(0,len)
	}
	gField.value = inputStr
	return true
}

// strip commas from money fields
function stripCommasAndDoll(inputStr) {
	var marker
	while (inputStr.indexOf("$") != -1) {
		marker = inputStr.indexOf("$")
		inputStr = inputStr.substring(0,marker) + inputStr.substring(marker+1,inputStr.length)
	}
	while (inputStr.indexOf(",") != -1) {
		marker = inputStr.indexOf(",")
		inputStr = inputStr.substring(0,marker) + inputStr.substring(marker+1,inputStr.length)
	}
	return inputStr
}

// restore commas for big dollar amounts
function reinsertCommas(inputStr) {
	var out = ""
	var len = inputStr.length
	for (var i = 0; i < len; i++) {
		if (i != 0 && i%3 == 0) {
			out = "," + out
		}
		out = inputStr.charAt(len-i-1) + out
	}
	return out
}

// strip decimal and trailing cents
function stripCents(inputStr) {
	var marker = inputStr.indexOf(".")
	if (marker != -1) {
		return inputStr.substring(0,marker)
	}
	return inputStr
}

// strip hyphens
function stripHyphens(inputStr) {
	var marker
	while (inputStr.indexOf("-") != -1) {
		marker = inputStr.indexOf("-")
		inputStr = inputStr.substring(0,marker) + inputStr.substring(marker+1,inputStr.length)
	}
	return inputStr
}

// strip phone number entry slashes and parentheses
function stripSlashParens(inputStr) {
	var marker
	while (inputStr.indexOf("/") != -1) {
		marker = inputStr.indexOf("/")
		inputStr = inputStr.substring(0,marker) + inputStr.substring(marker+1,inputStr.length)
	}
	while (inputStr.indexOf("(") != -1) {
		marker = inputStr.indexOf("(")
		inputStr = inputStr.substring(0,marker) + inputStr.substring(marker+1,inputStr.length)
	}
	while (inputStr.indexOf(")") != -1) {
		marker = inputStr.indexOf(")")
		inputStr = inputStr.substring(0,marker) + inputStr.substring(marker+1,inputStr.length)
	}
	while (inputStr.indexOf(" ") != -1) {
		marker = inputStr.indexOf(" ")
		inputStr = inputStr.substring(0,marker) + inputStr.substring(marker+1,inputStr.length)
	}
	return inputStr
}

function stripZeros(inputStr) {
	var result = inputStr
	while (result.substring(0,1) == "0") {
		result = result.substring(1,result.length)
	}
	return result
}

// insert hyphens into phone number
function restorePhoneHyphens(inputStr) {
	var n1 = inputStr.substring(0,3)
	var n2 = inputStr.substring(3,6)
	var n3 = inputStr.substring(6,10)
	return (n1 + "-" + n2 + "-" + n3)
}

// perform range checking on SSN
function isSSNRange(inputStr) {
	if (inputStr.substring(0,3) == "000" ||
		inputStr.substring(4,6) == "00"  ||
		inputStr.substring(7,11) == "0000") {
			alert("No segment of a valid Social Security Number can be all zeros.")
			gField.focus()
			gField.select()
			return false
	}
	if (parseInt(inputStr.substring(0,3), 10) >= 800) {
			alert("The first three digits of a valid Social Security Number must be less than \'800\'.")
			gField.focus()
			gField.select()
			return false
	}
	return true
}

// utility for isNum, isSSN, and isEIN validations
function isNum(n) {
	if (isPositiveInteger()) {
		var inputStr = gField.value
		if (inputStr.length != n) {
			alert("The selected field requires a number " + n + " digits long.")
			gField.focus()
			gField.select()
			return false
		}
		return true
	}
	return false
}

// States array
var USStates = new Array(53)
USStates["AL"] = "ALABAMA"
USStates["AK"] = "ALASKA"
USStates["AZ"] = "ARIZONA"
USStates["AR"] = "ARKANSAS"
USStates["CA"] = "CALIFORNIA"
USStates["CO"] = "COLORADO"
USStates["CT"] = "CONNECTICUT"
USStates["DE"] = "DELAWARE"
USStates["DC"] = "DISTRICT OF COLUMBIA"
USStates["FL"] = "FLORIDA"
USStates["FR"] = "FOREIGN"
USStates["GA"] = "GEORGIA"
USStates["HI"] = "HAWAII"
USStates["ID"] = "IDAHO"
USStates["IL"] = "ILLINOIS"
USStates["IN"] = "INDIANA"
USStates["IA"] = "IOWA"
USStates["KS"] = "KANSAS"
USStates["KY"] = "KENTUCKY"
USStates["LA"] = "LOUISIANA"
USStates["ME"] = "MAINE"
USStates["MD"] = "MARYLAND"
USStates["MA"] = "MASSACHUSETTS"
USStates["MI"] = "MICHIGAN"
USStates["MN"] = "MINNESOTA"
USStates["MS"] = "MISSISSIPPI"
USStates["MO"] = "MISSOURI"
USStates["MT"] = "MONTANA"
USStates["NE"] = "NEBRASKA"
USStates["NV"] = "NEVADA"
USStates["NH"] = "NEW HAMPSHIRE"
USStates["NJ"] = "NEW JERSEY"
USStates["NM"] = "NEW MEXICO"
USStates["NY"] = "NEW YORK"
USStates["NC"] = "NORTH CAROLINA"
USStates["ND"] = "NORTH DAKOTA"
USStates["OH"] = "OHIO"
USStates["OK"] = "OKLAHOMA"
USStates["OR"] = "OREGON"
USStates["PA"] = "PENNSYLVANIA"
USStates["PR"] = "PUERTO RICO"
USStates["RI"] = "RHODE ISLAND"
USStates["SC"] = "SOUTH CAROLINA"
USStates["SD"] = "SOUTH DAKOTA"
USStates["TN"] = "TENNESSEE"
USStates["TX"] = "TEXAS"
USStates["UT"] = "UTAH"
USStates["VT"] = "VERMONT"
USStates["VA"] = "VIRGINIA"
USStates["WA"] = "WASHINGTON"
USStates["WV"] = "WEST VIRGINIA"
USStates["WI"] = "WISCONSIN"
USStates["WY"] = "WYOMING"

// date field validation (called by other validation functions that specify minYear/maxYear)
function isDate(minYear,maxYear,minDays,maxDays) {
	var inputStr = gField.value
	// convert hyphen delimiters to slashes
	while (inputStr.indexOf("-") != -1) {
		inputStr = replaceString(inputStr,"-","/")
	}
	var delim1 = inputStr.indexOf("/")
	var delim2 = inputStr.lastIndexOf("/")
	if (delim1 != -1 && delim1 == delim2) {
		// there is only one delimiter in the string
		alert("The date entry is not in an acceptable format.\n\nYou can enter dates in the following formats: mmddyyyy, mm/dd/yyyy, or mm-dd-yyyy.  (If the month or date data is not available, enter \'01\' in the appropriate location.)")
		gField.focus()
		gField.select()
		return false
	}
	if (delim1 != -1) {
		// there are delimiters; extract component values
		var mm = parseInt(inputStr.substring(0,delim1),10)
		var dd = parseInt(inputStr.substring(delim1 + 1,delim2),10)
		var yyyy = parseInt(inputStr.substring(delim2 + 1, inputStr.length),10)
	} else {
		// there are no delimiters; extract component values
		var mm = parseInt(inputStr.substring(0,2),10)
		var dd = parseInt(inputStr.substring(2,4),10)
		var yyyy = parseInt(inputStr.substring(4,inputStr.length),10)
	}
	if (isNaN(mm) || isNaN(dd) || isNaN(yyyy)) {
		// there is a non-numeric character in one of the component values
		alert("The date entry is not in an acceptable format.\n\nYou can enter dates in the following formats: mmddyyyy, mm/dd/yyyy, or mm-dd-yyyy.")
		gField.focus()
		gField.select()
		return false
	}
	if (mm < 1 || mm > 12) {
		// month value is not 1 thru 12
		alert("Months must be entered between the range of 01 (January) and 12 (December).")
		gField.focus()
		gField.select()
		return false
	}
	if (dd < 1 || dd > 31) {
		// date value is not 1 thru 31
		alert("Days must be entered between the range of 01 and a maximum of 31 (depending on the month and year).")
		gField.focus()
		gField.select()
		return false
	}

	// validate year, allowing for checks between year ranges
	// passed as parameters from other validation functions
	if (yyyy < 100) {
		// entered value is two digits, which we allow for 1930-2029
		if (yyyy >= 30) {
			yyyy += 1900
		} else {
			yyyy += 2000
		}
	}

	var today = new Date()
	if (!minYear) {
		// function called with specific day range parameters
		var dateStr = new String(monthDayFormat(mm) + "/" + monthDayFormat(dd) + "/" + yyyy)
		var testDate = new Date(dateStr)
		if (testDate.getTime() < (today.getTime() + (minDays * 24 * 60 * 60 * 1000))) {
			alert("The most likely range for this entry begins " + minDays + " days from today.")
		}
		if (testDate.getTime() > today.getTime() + (maxDays * 24 * 60 * 60 * 1000)) {
			alert("The most likely range for this entry ends " + maxDays + " days from today.")
		}
	} else if (minYear && maxYear) {
		// function called with specific year range parameters
		if (yyyy < minYear || yyyy > maxYear) {
			// entered year is outside of range passed from calling function
			alert("The most likely range for this entry is between the years " + minYear + " and " + maxYear + ".  If your source data indicates a date outside this range, then enter that date.")
		}
	} else {
		// default year range (now set to (this year - 100) and (this year + 25)
		var thisYear = today.getYear()
		if (thisYear < 100) {
			thisYear += 1900
		}
		minYear = thisYear - 100
		maxYear = thisYear + 25
		if (yyyy < minYear || yyyy > maxYear) {
			alert("It is unusual for a date entry to be before " + minYear + " or after " + maxYear + ". Please verify this entry.")
		}
	}
	if (!checkMonthLength(mm,dd)) {
		gField.focus()
		gField.select()
		return false
	}
	if (mm == 2) {
		if (!checkLeapMonth(mm,dd,yyyy)) {
			gField.focus()
			gField.select()
			return false
		}
	}

	// put the Informix-friendly format back into the field
	gField.value = monthDayFormat(mm) + "/" + monthDayFormat(dd) + "/" + yyyy
	return true
}

// get JS-friendly year
function getYear() {
	var thisYear = (new Date()).getYear()
	thisYear = (thisYear < 1900) ? thisYear + 1900: thisYear
	return thisYear
}

// check the entered month for too high a value
function checkMonthLength(mm,dd) {
	var months = new Array("","January","February","March","April","May","June","July","August","September","October","November","December")
	if ((mm == 4 || mm == 6 || mm == 9 || mm == 11) && dd > 30) {
		alert(months[mm] + " has only 30 days.")
		return false
	} else if (dd > 31) {
		alert(months[mm] + " has only 31 days.")
		return false
	}
	return true
}

// check the entered February date for too high a value 
function checkLeapMonth(mm,dd,yyyy) {
	if (yyyy % 4 > 0 && dd > 28) {
		alert("February of " + yyyy + " has only 28 days.")
		return false
	} else if (dd > 29) {
		alert("February of " + yyyy + " has only 29 days.")
		return false
	}
	return true
}

// convert month or day number to string, padding with leading zero if needed
function monthDayFormat(val) {
	if (isNaN(val) || val == 0) {
		return "01"
	} else if (val < 10) {
		return "0" + val	
	}
	return "" + val
}

// Generic TIN component validator
function isTINComponent(len, type) {
	var inputStr = gField.value
	if (inputStr == null || inputStr == "") {return true}
	if (isNum(len)) {
		inputStr = parseInt(stripZeros(inputStr),10)
		if (inputStr == 0 || isNaN(inputStr)) {
			alert("All zeros are not allowed in any " + type + " number field.")
			gField.focus()
			gField.select()
			return false
		}
		return true
	}
	return false
}
// String utilities for search/replace functions
// extract front part of string prior to searchString
function getFront(mainStr,searchStr){
        foundOffset = mainStr.indexOf(searchStr)
        if (foundOffset == -1) {
                return null
        }
        return mainStr.substring(0,foundOffset)
}

// extract back end of string after searchString
function getEnd(mainStr,searchStr) {
        foundOffset = mainStr.indexOf(searchStr)
        if (foundOffset == -1) {
                return null
        }
        return mainStr.substring(foundOffset+searchStr.length,mainStr.length)
}

// insert insertString immediately before searchString
function insertString(mainStr,searchStr,insertStr) {
        var front = getFront(mainStr,searchStr)
        var end = getEnd(mainStr,searchStr)
        if (front != null && end != null) {
                return front + insertStr + searchStr + end
        }
        return null
}

// remove deleteString
function deleteString(mainStr,deleteStr) {
        return replaceString(mainStr,deleteStr,"")
}

// replace searchString with replaceString

function replaceString(mainStr,searchStr,replaceStr) {
        var front = getFront(mainStr,searchStr)
        var end = getEnd(mainStr,searchStr)
        if (front != null && end != null) {
                return front + replaceStr + end
        }
        return null
}


/******************************************
   Begin validation dispatching mechanism
******************************************/
// validation function object constructor
function dispatcher(validationFunc) {
   this.doValidate = validationFunc
}
var dispatchLookup = new Array
dispatchLookup["isNotEmpty"] = new dispatcher(isNotEmpty)
dispatchLookup["isPositiveInteger"] = new dispatcher(isPositiveInteger)
dispatchLookup["isDollarsOnly8Commas"] = new dispatcher(isDollarsOnly8Commas)
dispatchLookup["isUSState"] = new dispatcher(isUSState)
dispatchLookup["maxString15"] = new dispatcher(maxString15)
dispatchLookup["maxString25"] = new dispatcher(maxString25)
dispatchLookup["maxString30"] = new dispatcher(maxString30)
dispatchLookup["maxString35"] = new dispatcher(maxString35)
dispatchLookup["isNYU"] = new dispatcher(isNYU)
dispatchLookup["isConfirmed"] = new dispatcher(isConfirmed)
dispatchLookup["isZip"] = new dispatcher(isZip)
dispatchLookup["isPhone"] = new dispatcher(isPhone)
dispatchLookup["isSSN1"] = new dispatcher(isSSN1)
dispatchLookup["isSSN2"] = new dispatcher(isSSN2)
dispatchLookup["isSSN3"] = new dispatcher(isSSN3)
dispatchLookup["isM5_P10Date"] = new dispatcher(isM5_P10Date)
dispatchLookup["isDateFormat"] = new dispatcher(isDateFormat)

// main validation function called by form event handlers
function validate(frame, field, method) {
	gFrame = frame
	gField = eval("window." + frame.name + ".document.forms[0]." + field.name)
	var args = validate.arguments
	for (i = 2; i < args.length; i++) {
		if (!dispatchLookup[args[i]].doValidate()) {
			return false
		}
	}
	return true
}


